package org.geektimes.web.mvc.header;

import org.apache.commons.lang.StringUtils;
import org.geektimes.web.mvc.controller.Controller;
import org.geektimes.web.mvc.controller.PageController;
import org.geektimes.web.mvc.controller.RestController;
import org.geektimes.web.mvc.header.matedata.HandlerMethodInfo;

import javax.servlet.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.Path;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;

import static java.util.Arrays.asList;
import static org.apache.commons.lang.StringUtils.substringAfter;

public class FrontControllerServlet extends HttpServlet implements HandlerMethodRegistry {


	private String jspPrefix = "WEB-INF";

	private static final Logger logger = Logger.getLogger("");
	/**
	 * 请求路径和 Controller 的映射关系缓存
	 */
	@Deprecated
	private Map<String, Controller> controllersMapping = new HashMap<>();

	/**
	 * 请求路径和 {@link HandlerMethodInfo} 映射关系缓存
	 */
	private Map<String, HandlerMethodInfo> handleMethodInfoMapping = new HashMap<>();


	private WebMvcSupport webMvcSupport = new WebMvcSupport();

	/**
	 * 初始化 Servlet
	 *
	 * @param servletConfig
	 */
	public void init(ServletConfig servletConfig) {
		//初始化参数
		jspPrefix = servletConfig.getInitParameter("jsp-prefix");

		webMvcSupport.loadHandlerMethods(this);
		logger.log(Level.INFO, "FrontControllerServlet init complete");
	}

	/**
	 * 获取jsp前缀
	 * @return
	 */
	public String getJspPrefix() {
		return jspPrefix;
	}


	/**
	 * 读取所有的 RestController 的注解元信息 @Path
	 * 利用 ServiceLoader 技术（Java SPI）
	 */
   /* private void initHandleMethods() {
        for (Controller controller : ServiceLoader.load(Controller.class)) {
            Class<?> controllerClass = controller.getClass();
            Path pathFromClass = controllerClass.getAnnotation(Path.class);
            String requestPath = pathFromClass.value();
            Method[] publicMethods = controllerClass.getMethods();
            // 处理方法支持的 HTTP 方法集合
            for (Method method : publicMethods) {
                Set<String> supportedHttpMethods = findSupportedHttpMethods(method);
                Path pathFromMethod = method.getAnnotation(Path.class);
                if (pathFromMethod != null) {
                    requestPath += pathFromMethod.value();
                }
                handleMethodInfoMapping.put(requestPath, new HandlerMethodInfo(requestPath, method, supportedHttpMethods));
            }
            controllersMapping.put(requestPath, controller);
        }
    }*/

	/**
	 * 获取处理方法中标注的 HTTP方法集合
	 *
	 * @param method 处理方法
	 * @return
	 */
	@Deprecated
	private Set<String> findSupportedHttpMethods(Method method) {
		Set<String> supportedHttpMethods = new LinkedHashSet<>();
		for (Annotation annotationFromMethod : method.getAnnotations()) {
			HttpMethod httpMethod = annotationFromMethod.annotationType().getAnnotation(HttpMethod.class);
			if (httpMethod != null) {
				supportedHttpMethods.add(httpMethod.value());
			}
		}

		if (supportedHttpMethods.isEmpty()) {
			supportedHttpMethods.addAll(asList(HttpMethod.GET, HttpMethod.POST,
					HttpMethod.PUT, HttpMethod.DELETE, HttpMethod.HEAD, HttpMethod.OPTIONS));
		}

		return supportedHttpMethods;
	}

	/**
	 * SCWCD
	 *
	 * @param request
	 * @param response
	 * @throws ServletException
	 * @throws IOException
	 */
	@Override
	public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		preExecute(request , response);
		webMvcSupport.execute(this, request, response);
		postExecute(request , response);
//        // 建立映射关系
//        // requestURI = /a/hello/world
//        String requestURI = request.getRequestURI();
//        // contextPath  = /a or "/" or ""
//        String servletContextPath = request.getContextPath();
//        String prefixPath = servletContextPath;
//        // 映射路径（子路径）
//        String requestMappingPath = substringAfter(requestURI,
//                StringUtils.replace(prefixPath, "//", "/"));
//        // 映射到 Controller
//        Controller controller = controllersMapping.get(requestMappingPath);
//
//        if (controller != null) {
//
//            HandlerMethodInfo handlerMethodInfo = handleMethodInfoMapping.get(requestMappingPath);
//
//            try {
//                if (handlerMethodInfo != null) {
//
//                    String httpMethod = request.getMethod();
//
//                    if (!handlerMethodInfo.getSupportedHttpMethods().contains(httpMethod)) {
//                        // HTTP 方法不支持
//                        response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
//                        return;
//                    }
//
//                    if (controller instanceof PageController) {
//                        PageController pageController = PageController.class.cast(controller);
//                        String viewPath = pageController.execute(request, response);
//                        // 页面请求 forward
//                        // request -> RequestDispatcher forward
//                        // RequestDispatcher requestDispatcher = request.getRequestDispatcher(viewPath);
//                        // ServletContext -> RequestDispatcher forward
//                        // ServletContext -> RequestDispatcher 必须以 "/" 开头
//                        ServletContext servletContext = request.getServletContext();
//                        if (!viewPath.startsWith("/")) {
//                            viewPath = "/" + viewPath;
//                        }
//                        RequestDispatcher requestDispatcher = servletContext.getRequestDispatcher(viewPath);
//                        requestDispatcher.forward(request, response);
//                        return;
//                    } else if (controller instanceof RestController) {
//                        // TODO
//                    }
//
//                }
//            } catch (Throwable throwable) {
//                if (throwable.getCause() instanceof IOException) {
//                    throw (IOException) throwable.getCause();
//                } else {
//                    throw new ServletException(throwable.getCause());
//                }
//            }
//        }
	}

	private void preExecute(HttpServletRequest request, HttpServletResponse response) {
		///TODO 扩展实现 如 跨域处理等等
	}

	private void postExecute(HttpServletRequest request, HttpServletResponse response) {
		///TODO 扩展实现
	}


	@Override
	public void registerHandlerMethod(HandlerMethodInfo handlerMethodInfo) {
		List<String> handlerMethodKeys = getHandlerMethodKeys(handlerMethodInfo);
		for (String key : handlerMethodKeys) {
			HandlerMethodInfo handlerMethodInfo1 = handleMethodInfoMapping.putIfAbsent(key, handlerMethodInfo);
			if (handlerMethodInfo1 != null) {
				throw new IllegalArgumentException(String.format("web method[%s] 重复注册", handlerMethodInfo.toString()));
			}
			logger.log(Level.INFO, handlerMethodInfo.toString());
		}

	}

	@Override
	public HandlerMethodInfo get(String path) {

		return this.handleMethodInfoMapping.get(path);
	}

	@Override
	public Map<String, HandlerMethodInfo> getHandlerMethods() {
		return this.handleMethodInfoMapping;
	}


	/**
	 * 获取key
	 *
	 * @param request
	 * @param path
	 * @return
	 */
	public String getHandlerMethodKey(HttpServletRequest request, String path) {
		return path + ":" + request.getMethod();
	}

	/**
	 * 获取key
	 *
	 * @param handlerMethodInfo
	 * @return
	 */
	private List<String> getHandlerMethodKeys(HandlerMethodInfo handlerMethodInfo) {
		return handlerMethodInfo
				.getSupportedHttpMethods()
				.stream()
				.map(item -> handlerMethodInfo.getRequestPath() + ":" + item)
				.distinct()
				.collect(Collectors.toList());
	}


//    private void beforeInvoke(Method handleMethod, HttpServletRequest request, HttpServletResponse response) {
//
//        CacheControl cacheControl = handleMethod.getAnnotation(CacheControl.class);
//
//        Map<String, List<String>> headers = new LinkedHashMap<>();
//
//        if (cacheControl != null) {
//            CacheControlHeaderWriter writer = new CacheControlHeaderWriter();
//            writer.write(headers, cacheControl.value());
//        }
//    }
}
